package com.share.device.emqx.handler.impl;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.share.common.core.constant.DeviceConstants;
import com.share.device.domain.Cabinet;
import com.share.device.domain.CabinetSlot;
import com.share.device.domain.PowerBank;
import com.share.device.emqx.annotation.GuiguEmqx;
import com.share.device.emqx.constant.EmqxConstants;
import com.share.device.emqx.handler.MassageHandler;
import com.share.device.service.ICabinetService;
import com.share.device.service.ICabinetSlotService;
import com.share.device.service.IPowerBankService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Component
@GuiguEmqx(topic = EmqxConstants.TOPIC_PROPERTY_POST)
@RequiredArgsConstructor
public class PropertyPostHandler implements MassageHandler {

    private final ICabinetService cabinetService;

    private final IPowerBankService powerBankService;

    private final ICabinetSlotService cabinetSlotService;

    private final RedisTemplate redisTemplate;

    private static final String REDIS_KEY_PREFIX = "property:post:";

    /**
     * 处理设备属性上报消息
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void handleMessage(JSONObject message) {
        log.info("开始处理属性上报消息: {}", message.toJSONString());

        // 1. 消息幂等性处理
        String messageNo = message.getString("mNo");
        if (StringUtils.isEmpty(messageNo)) {
            log.warn("消息缺少必要参数: mNo");
            return;
        }
        String redisKey = REDIS_KEY_PREFIX + messageNo;

        try {
            Boolean isExist = redisTemplate.opsForValue().setIfAbsent(redisKey, messageNo, 1, TimeUnit.HOURS);
            if (Boolean.FALSE.equals(isExist)) {
                log.info("重复消息请求，消息编号: {}", messageNo);
                return;
            }
            log.debug("消息幂等性校验通过，消息编号: {}", messageNo);
        } catch (Exception e) {
            log.error("Redis操作异常，消息可能重复处理，消息编号: {}", messageNo, e);
        }

        // 2. 解析消息参数
        String cabinetNo = message.getString("cNo");
        String powerBankNo = message.getString("pNo");
        String slotNo = message.getString("sNo");
        BigDecimal electricity = message.getBigDecimal("ety");

        if (StringUtils.isEmpty(cabinetNo) || StringUtils.isEmpty(powerBankNo) || StringUtils.isEmpty(slotNo) || electricity == null) {
            log.warn("属性上报消息缺少必要参数: 柜机编号={}, 充电宝编号={}, 插槽编号={}, 电量={}", cabinetNo, powerBankNo, slotNo, electricity);
            return;
        }
        log.debug("消息参数解析完成: 柜机={}, 充电宝={}, 插槽={}, 电量={}", cabinetNo, powerBankNo, slotNo, electricity);

        // 3. 获取业务对象
        Cabinet cabinet = cabinetService.getBtCabinetNo(cabinetNo);
        if (cabinet == null) {
            log.warn("未找到对应柜机，柜机编号: {}", cabinetNo);
            return;
        }

        PowerBank powerBank = powerBankService.getByPowerBankNo(powerBankNo);
        if (powerBank == null) {
            log.warn("未找到对应充电宝，充电宝编号: {}", powerBankNo);
            return;
        }
        log.debug("成功获取业务对象: 柜机ID={}, 充电宝ID={}", cabinet.getId(), powerBank.getId());

        // 4. 更新充电宝状态和电量
        try {
            powerBank.setElectricity(electricity);
            String status = electricity.subtract(DeviceConstants.ELECTRICITY_MIN).doubleValue() > 0 ? "1" : "3";
            powerBank.setStatus(status);
            powerBankService.updateById(powerBank);
            log.info("充电宝状态更新完成，编号: {}, 电量: {}, 状态: {}", powerBank.getPowerBankNo(), electricity, status);
        } catch (Exception e) {
            log.error("更新充电宝状态失败，编号: {}", powerBank.getPowerBankNo(), e);
            throw new RuntimeException("更新充电宝状态失败", e);
        }

        // 5. 更新柜机可用充电宝数量
        try {
            List<CabinetSlot> cabinetSlotList = cabinetSlotService.list(
                    new LambdaQueryWrapper<CabinetSlot>()
                            .eq(CabinetSlot::getStatus, "1")
                            .eq(CabinetSlot::getCabinetId, cabinet.getId())
                            .select(CabinetSlot::getPowerBankId)
            );

            List<Long> powerBankIdList = cabinetSlotList.stream()
                    .map(CabinetSlot::getPowerBankId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());

            if (!powerBankIdList.isEmpty()) {
                Long availableNum = powerBankService.count(
                        new LambdaQueryWrapper<PowerBank>()
                                .in(PowerBank::getId, powerBankIdList)
                                .eq(PowerBank::getStatus, "1")
                );
                cabinet.setAvailableNum(availableNum.intValue());
            } else {
                cabinet.setAvailableNum(0);
            }

            cabinet.setUpdateTime(new Date());
            cabinetService.updateById(cabinet);

            log.info("柜机可用充电宝数量更新完成，柜机编号: {}, 可用数量: {}",
                    cabinet.getCabinetNo(), cabinet.getAvailableNum());
        } catch (Exception e) {
            log.error("更新柜机可用数量失败，柜机ID: {}", cabinet.getId(), e);
            throw new RuntimeException("更新柜机可用数量失败", e);
        }

        log.info("属性上报消息处理完成，消息编号: {}", messageNo);
    }
}
